#include"stdafx.h"
#include "d3ddependences.h"

LPDIRECT3D9             g_pD3D = NULL; // Used to create the D3DDevice
LPDIRECT3DDEVICE9       g_pd3dDevice = NULL; // Our rendering device


WLRenderInfo* WLD3DRender::GetRenderInfo(unsigned int ActorID)
{
	for ( unsigned int i = 0; i<m_RenderInfoArray.size(); i++ )
	{
		if ( m_RenderInfoArray[i].m_ActorID == ActorID )
			return &(m_RenderInfoArray[i]);
	}
	return NULL;
}
WLD3DRenderFrame* WLD3DRender::GetFrame(unsigned int ActorID)
{
	for ( unsigned int i = 0; i<m_FrameArray.size(); i++ )
	{
		if ( m_FrameArray[i].m_FrameID == ActorID )
			return &(m_FrameArray[i]);
	}
	return NULL;
}
void WLD3DRender::ReleaseFrame(WLD3DRenderFrame* pFrame,ECR_CLEARFLAG ClearFlag)
{	
	for ( unsigned int i = 0; i<pFrame->m_FrameDataArray.size(); i++ )
	{
		WLD3DFrameData& Data = pFrame->m_FrameDataArray[i];
		if ( ClearFlag&ECR_Vertex && Data.m_pVB )
		{
			Data.m_pVB->Release();
			Data.m_pVB = NULL;
		}
		if ( ClearFlag&ECR_Index && Data.m_pIB )
		{
			Data.m_pIB->Release();
			Data.m_pIB = NULL;
		}
	}
}
bool WLD3DRender::UpdateFrame(WLRenderInfo& Info)
{
	WLMatrix16 Rot,Transform,LocalToWorld;
	WLD3DRenderFrame* pFrame = GetFrame(Info.m_ActorID);
	bool isNewFrame = false;
	WLD3DRenderFrame tmpFrame;
	tmpFrame.m_FrameID = Info.m_ActorID;
	if ( !pFrame )
	{
		pFrame = &tmpFrame;
		isNewFrame = true;
	}
	else if ( Info.m_ClearFlag & ECR_Stream )
	{
		ReleaseFrame(pFrame,Info.m_ClearFlag);
	}
	if ( Info.m_ClearFlag & ECR_Matrix )
	{
		D3DXMatrixRotationQuaternion(&Rot,&(Info.m_Rotation));
		D3DXMatrixTranslation(&Transform,Info.m_Position.x,Info.m_Position.y,Info.m_Position.z);
		D3DXMatrixMultiply(&(LocalToWorld),&Rot,&Transform);
		pFrame->m_LocalToWorld = LocalToWorld;
	}
	Info.m_Primitive->PreRender();
	for ( unsigned int i = 0; i<Info.m_Primitive->GetDataArray().size(); i++ )
	{
		bool isNewFrameData = false;
		WLD3DFrameData tmpFrameData;
		WLD3DFrameData* pFrameData = NULL;
		if ( i+1 > (pFrame->m_FrameDataArray.size()) )
		{
			pFrameData = &tmpFrameData;
			isNewFrameData = true;
		}
		else
		{
			pFrameData = &(pFrame->m_FrameDataArray[i]);
		}
		pFrameData->Revision = Info.m_Revision;
		WLPrimData& PrimData = Info.m_Primitive->GetDataArray()[i];
		if ( Info.m_ClearFlag & ECR_Vertex  )
		{
			pFrameData->m_VertSize = PrimData.GetVertexSize();
			unsigned int size = PrimData.GetVertexSize()*PrimData.m_PositionData.size();
			DWORD VertFormat = PrimData.GetFormat();
			pFrameData->m_VertexFormat = VertFormat;
			pFrameData->m_iVertCount = PrimData.m_PositionData.size();

			if( FAILED( g_pd3dDevice->CreateVertexBuffer( size,0 /*Usage*/, PrimData.GetFormat(), D3DPOOL_DEFAULT, &pFrameData->m_pVB, NULL ) ) )
			{
				pFrameData->m_pVB = NULL;
				return false;
			}

			// Lock the buffer to gain access to the vertices 
			VOID* pVertices;
			if ( FAILED(pFrameData->m_pVB->Lock(0,  size, 
				(VOID**)(&(pVertices)), 0  )))
				return false;
			for ( unsigned int j = 0; j<PrimData.m_PositionData.size(); j++ )
			{
				unsigned int Index = 0;
				float* Buffer = &((float*)pVertices)[pFrameData->m_VertSize/sizeof(float)*j];
				if ( VertFormat&FVF_XYZ )
				{
					WLVector3* Vec = PrimData.GetPosition(j);
					Buffer[Index++] = Vec->x;
					Buffer[Index++] = -Vec->y;
					Buffer[Index++] = Vec->z;
				}
				if ( VertFormat & FVF_DIFFUSE )
				{
					((DWORD*)Buffer)[Index++] = *(PrimData.GetColor(j));
				}
				if ( VertFormat & FVF_NORMAL )
				{
					WLVector3* Vec = PrimData.GetNormal(j);
					Buffer[Index++] = Vec->x;
					Buffer[Index++] = -Vec->y;
					Buffer[Index++] = Vec->z;
				}
				if ( VertFormat & FVF_TEX1 )
				{
					Buffer[Index++] =  PrimData.GetUVW(j)->x;
					Buffer[Index++] =  1.f - PrimData.GetUVW(j)->y;
					Buffer[Index++] =  PrimData.GetUVW(j)->z;
			
				}
			}
			pFrameData->m_pVB->Unlock();
		
		}
		if ( PrimData.m_Faces.size()>0 )
		{
			if ( Info.m_ClearFlag & ECR_Index )
			{
				if( FAILED( g_pd3dDevice->CreateIndexBuffer( PrimData.m_Faces.size()*3 *sizeof(INT),
				   D3DUSAGE_WRITEONLY, D3DFMT_INDEX32, D3DPOOL_DEFAULT, 
				   &(pFrameData->m_pIB), NULL ) ) )
				{
					pFrameData->m_pIB = NULL;
					return false;
				}
				VOID* pIB;
				if (FAILED(pFrameData->m_pIB->Lock(0, PrimData.m_Faces.size()*3 *sizeof(INT),&pIB,0)))
				{
					return false;
				}

				memcpy(pIB,  PrimData.m_Faces.data(), PrimData.m_Faces.size()*3*sizeof(INT));

				pFrameData->m_pIB->Unlock();
			}
			//FrameData.m_iPrimitiveCount = PrimData.m_Faces.size();
			D3DXCreateTextureFromFileA(g_pd3dDevice,PrimData.m_pMaterial->m_TexDiffuse.data(),&pFrameData->m_TxDiffuse);
		}
		if ( Info.m_ClearFlag & ECR_Stream )
		{
			pFrameData->m_iPrimitiveCount = PrimData.GetPrimitiveCount();
			pFrameData->m_PrimitiveType = D3DPRIMITIVETYPE(PrimData.GetPrimitiveType());
			if ( isNewFrameData )
				pFrame->m_FrameDataArray.push_back(*pFrameData);
			pFrameData = NULL;
		}
	}
	Info.m_Primitive->PostRender();
	if ( isNewFrame )
	{
		m_FrameArray.push_back(*pFrame);
		pFrame = NULL;
	}
	return true;
}
void WLD3DRender::UpdateAllRenderInfos()
{
	for ( unsigned int i = 0; i<m_RenderInfoArray.size(); i++ )
	{
		if ( m_RenderInfoArray[i].m_Revision != m_RenderInfoArray[i].m_Primitive->GetReversion() )
		{
			m_RenderInfoArray[i].m_ClearFlag = ECR_Vertex;
			m_RenderInfoArray[i].m_Revision = m_RenderInfoArray[i].m_Primitive->GetReversion();
		}
	}
}

void WLD3DRender::UpdateAllFrames()
{
	for ( unsigned int i = 0; i<m_RenderInfoArray.size(); i++ )
	{
		if ( m_RenderInfoArray[i].m_ClearFlag == ECR_None )
			continue;
		UpdateFrame(m_RenderInfoArray[i]);
		m_RenderInfoArray[i].m_ClearFlag = ECR_None;
	}
}
VOID WLD3DRender::SetupMatrices()
{

    D3DXVECTOR3 vEyePt( 0.0f, 50.0f,-100.0f );
    D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f );
    D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f );
    D3DXMATRIXA16 matView;
    D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec );
    g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );

    D3DXMATRIXA16 matProj;
    D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI / 3, 1.0f, 1.0f, 65536.0f );
    g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
}

bool WLD3DRender::InitDevice(HWND hWnd)
{
    // Create the D3D object.
    if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
        return false;

    // Set up the structure used to create the D3DDevice. Since we are now
    // using more complex geometry, we will create a device with a zbuffer.
    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory( &d3dpp, sizeof( d3dpp ) );
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
    d3dpp.EnableAutoDepthStencil = TRUE;
    d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;

    // Create the D3DDevice
    if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
                                      D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                      &d3dpp, &g_pd3dDevice ) ) )
    {
        return false;
    }

    // Turn off culling
    g_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );

    // Turn off D3D lighting
    g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );

    // Turn on the zbuffer
    g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE );

	return true;
}

void WLD3DRender::CleanUp()
{
	for ( unsigned int i = 0; i<m_FrameArray.size(); i++ )
	{
		for ( unsigned int j = 0; j<m_FrameArray[i].m_FrameDataArray.size(); j++ )
		{
			WLD3DFrameData& Data = m_FrameArray[i].m_FrameDataArray[j];
			if ( Data.m_pVB )
			{
				Data.m_pVB->Release();
				Data.m_pVB = NULL;
			}
			if ( Data.m_TxDiffuse )
			{
				Data.m_TxDiffuse->Release();
				Data.m_TxDiffuse = NULL;
			}
			if ( Data.m_pIB )
			{
				Data.m_pIB->Release();
				Data.m_pIB = NULL;
			}
		}
	}
    if( g_pd3dDevice != NULL )
        g_pd3dDevice->Release();

    if( g_pD3D != NULL )
        g_pD3D->Release();
}

VOID SetupLights()
{
    // Set up a material. The material here just has the diffuse and ambient
    // colors set to yellow. Note that only one material can be used at a time.
    D3DMATERIAL9 mtrl;
    ZeroMemory( &mtrl, sizeof( D3DMATERIAL9 ) );
    mtrl.Diffuse.r = mtrl.Ambient.r = 1;
    mtrl.Diffuse.g = mtrl.Ambient.g = 1;
    mtrl.Diffuse.b = mtrl.Ambient.b = 1;
    mtrl.Diffuse.a = mtrl.Ambient.a = 1;
    g_pd3dDevice->SetMaterial( &mtrl );

    // Set up a white, directional light, with an oscillating direction.
    // Note that many Lights may be active at a time (but each one slows down
    // the rendering of our scene). However, here we are just using one. Also,
    // we need to set the D3DRS_LIGHTING renderstate to enable lighting
    D3DXVECTOR3 vecDir;
    D3DLIGHT9 light;
    ZeroMemory( &light, sizeof( D3DLIGHT9 ) );
    light.Type = D3DLIGHT_DIRECTIONAL;
    light.Diffuse.r = 1;
    light.Diffuse.g = 1;
    light.Diffuse.b = 1;

	vecDir = D3DXVECTOR3(cosf(timeGetTime()/360.0f),
                     0.0f,
                     sinf(timeGetTime()/360.0f) );

    D3DXVec3Normalize( ( D3DXVECTOR3* )&light.Direction, &vecDir );
    light.Range = 1000.0f;
    g_pd3dDevice->SetLight( 0, &light );
    g_pd3dDevice->LightEnable( 0, TRUE );
    g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE );

    // Finally, turn on some ambient light.
    g_pd3dDevice->SetRenderState( D3DRS_AMBIENT, 0 );
}
void WLD3DRender::PreRender()
{
	UpdateAllRenderInfos();
	UpdateAllFrames();
}
 void WLD3DRender::Render()
 {
     // Clear the backbuffer and the zbuffer
    g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
                         D3DCOLOR_XRGB( 0, 0, 0xff ), 1.0f, 0 );
    // Begin the scene
    if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
    {
		SetupMatrices();
		SetupLights();
		g_pd3dDevice->SetRenderState(D3DRS_ZWRITEENABLE,TRUE);
		g_pd3dDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESS );
		g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);

		for ( unsigned int i = 0; i<m_FrameArray.size(); i++ )
		{
			//break;
			// Set up world matrix
			D3DXMATRIXA16 matWorld;
			D3DXMatrixIdentity( &matWorld );
			//D3DXMatrixRotationX( &matWorld, timeGetTime() / 500.0f );
			D3DXMatrixMultiply(&matWorld,&matWorld,&(m_FrameArray[i].m_LocalToWorld));
			g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );
			for ( unsigned int j = 0; j<m_FrameArray[i].m_FrameDataArray.size(); j++ )
			{
				WLD3DFrameData& Data = m_FrameArray[i].m_FrameDataArray[j];
				g_pd3dDevice->SetTexture(0,Data.m_TxDiffuse);

				g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE );
				g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
				g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
				g_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
				g_pd3dDevice->SetRenderState(D3DRS_CULLMODE,D3DCULL_NONE);

				g_pd3dDevice->SetStreamSource( 0, Data.m_pVB, 0, Data.m_VertSize );
				g_pd3dDevice->SetFVF( Data.m_VertexFormat );
				//g_pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
				if ( Data.m_pIB )
				{
					g_pd3dDevice->SetIndices(Data.m_pIB);
					g_pd3dDevice->DrawIndexedPrimitive( Data.m_PrimitiveType, 0,0,Data.m_iVertCount,0,Data.m_iPrimitiveCount );
				}
				else
					g_pd3dDevice->DrawPrimitive( Data.m_PrimitiveType, 0, Data.m_iPrimitiveCount );
				}
		}
		// End the scene
        g_pd3dDevice->EndScene();
    }

    // Present the backbuffer contents to the display
    g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
 
 
 }
